package com.pyf.ui.bar.top;

import com.pyf.ui.bar.common.IBarLayout;
import com.pyf.ui.util.DisplayUtils;

import java.util.ArrayList;
import java.util.List;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.ScrollView;
import ohos.agp.utils.Rect;
import ohos.app.Context;

/**
 * 顶部导航栏的封装，第一个泛型就是底部导航栏的条目，第二个泛型就是每个条目的数据
 *
 * @author 裴云飞
 * @date 2020/12/26
 */
public class TopNavigationBar extends ScrollView implements IBarLayout<TopBar, TopBarInfo<?>> {

    private List<OnBarSelectedListener<TopBarInfo<?>>> mBarSelectedListeners = new ArrayList<>();
    /**
     * 所有的条目
     */
    private List<TopBarInfo<?>> infoList;
    /**
     * 当前选中的条目
     */
    private TopBarInfo<?> selectedInfo;
    /**
     * 每个条目的宽度
     */
    private int tabWidth;

    public TopNavigationBar(Context context) {
        this(context, null);
    }

    public TopNavigationBar(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public TopNavigationBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    @Override
    public TopBar findBar(TopBarInfo<?> info) {
        DirectionalLayout rootLayout = getRootLayout(false);
        for (int i = 0; i < rootLayout.getChildCount(); i++) {
            Component component = rootLayout.getComponentAt(i);
            if (component instanceof TopBar) {
                TopBar topBar = (TopBar) component;
                if (topBar.getBarInfo() == info) {
                    return topBar;
                }
            }
        }
        return null;
    }

    @Override
    public void addBarSelectedChangeListener(OnBarSelectedListener<TopBarInfo<?>> listener) {
        mBarSelectedListeners.add(listener);
    }

    /**
     * 默认选中的条目
     *
     * @param defaultInfo 默认选中的条目
     */
    @Override
    public void defaultSelected(TopBarInfo<?> defaultInfo) {
        onSelected(defaultInfo);
    }

    /**
     * 初始化所有的条目
     *
     * @param infoList
     */
    @Override
    public void initInfo(List<TopBarInfo<?>> infoList) {
        if (infoList == null || infoList.isEmpty()) {
            return;
        }
        this.infoList = infoList;
        removeComponent();
        selectedInfo = null;
        addTopBar();
    }

    /**
     * 添加条目
     */
    private void addTopBar() {
        DirectionalLayout rootLayout = getRootLayout(true);
        for (int i = 0; i < infoList.size(); i++) {
            TopBarInfo<?> topBarInfo = infoList.get(i);
            TopBar topBar = new TopBar(getContext());
            topBar.setBarInfo(topBarInfo);
            rootLayout.addComponent(topBar);
            mBarSelectedListeners.add(topBar);
            topBar.setClickedListener(new ClickedListener() {
                @Override
                public void onClick(Component component) {
                    onSelected(topBarInfo);
                }
            });
        }
    }

    private void onSelected(TopBarInfo<?> nextInfo) {
        for (OnBarSelectedListener<TopBarInfo<?>> listener : mBarSelectedListeners) {
            listener.onBarSelectedChange(infoList.indexOf(nextInfo), selectedInfo, nextInfo);
        }
        selectedInfo = nextInfo;
        // 自动滚动
        autoScroll(nextInfo);
    }

    /**
     * 自动滚动，实现点击的位置能够自动滚动，以展示前后两个
     *
     * @param nextInfo
     */
    private void autoScroll(TopBarInfo<?> nextInfo) {
        TopBar tabTop = findBar(nextInfo);
        if (tabTop == null) return;
        int index = infoList.indexOf(nextInfo);
        // 获取点击的控件在屏幕的位置，坐标原点为屏幕的左上角
        int[] loc = tabTop.getLocationOnScreen();
        int scrollWidth;
        if (tabWidth == 0) {
            tabWidth = tabTop.getWidth();
        }
        // 判断点击了屏幕左侧还是右侧
        if ((loc[0] + tabWidth / 2) > DisplayUtils.getDisplayWidthInPx(getContext()) / 2) {
            // 点击屏幕右侧
            scrollWidth = rangeScrollWidth(index, 2);
        } else {
            // 点击屏幕左侧
            scrollWidth = rangeScrollWidth(index, -2);
        }
        // 滚动
        fluentScrollBy(scrollWidth, 0);
    }

    /**
     * 获取可滚动的范围
     *
     * @param index 从第几个开始滚动
     * @param range 向坐或者向右滚动多少个tab
     * @return 可滚动的范围
     */
    private int rangeScrollWidth(int index, int range) {
        int scrollWidth = 0;
        for (int i = 0; i <= Math.abs(range); i++) {
            int next;
            if (range < 0) {
                next = range + i + index;
            } else {
                next = range - i + index;
            }
            if (next >= 0 && next < infoList.size()) {
                if (range < 0) {
                    scrollWidth -= scrollWidth(next, false);
                } else {
                    scrollWidth += scrollWidth(next, true);
                }
            }
        }
        return scrollWidth;

    }

    /**
     * 指定位置的控件可滚动的距离
     *
     * @param index 指定位置的控件
     * @param toRight 是否点击了屏幕右侧
     * @return 可滚动的距离
     */
    private int scrollWidth(int index, boolean toRight) {
        TopBar target = findBar(infoList.get(index));
        if (target == null) return 0;
        Rect rect = new Rect();
        /**
         * getSelfVisibleRect获取组件自身的可见区域，左边原点为组件自身的左上角
         * 当组件在屏幕中完全可见时，rect的值为（0，0，组件的宽度，组件的高度）
         * 当组件在屏幕中完全不可见时，rect的值为（0，0，0，0）
         * 当组件在屏幕右边部分可见，rect的值为（0，0，组件的宽度 - 组件不可见的部分，组件的高度）
         * 当组件在屏幕左边部分可见，rect的值为（组件不可见的部分，0，组件的宽度，组件的高度）
         *
         * 所以，不管在屏幕左侧还是在屏幕右侧点击了条目，如果组件的可见区域都为0时，说明组件在屏幕中安全不可见。
         * 当我们在屏幕右侧点击了条目，如果组件的可见区域都为0时，说明组件在屏幕中安全不可见，否则说明组件在屏幕右边显示了部分。
         * 当我们在屏幕左侧点击了条目，如果组件的可见区域都为0时，说明组件在屏幕中安全不可见；
         * 如果组件的左边区域大于0，说明组件在屏幕左侧显示了部分，否则说明组件完全可见。
         */
        target.getSelfVisibleRect(rect);
        if (toRight) {// 点击屏幕右侧
            if (rect.isEmpty()) {
                // 组件完全没有显示
                return tabWidth;
            } else {
                // 组件显示部分，减去已显示的宽度
                return tabWidth - rect.right;
            }
        } else {
            if (rect.isEmpty()) {
                // 组件完全没有显示
                return tabWidth;
            } else if (rect.left > 0) {
                // 显示部分
                return rect.left;
            }
            // 组件完全显示
            return 0;
        }
    }
    
    private DirectionalLayout getRootLayout(boolean clear) {
        DirectionalLayout rootLayout = (DirectionalLayout) getComponentAt(0);
        if (rootLayout == null) {
            rootLayout = new DirectionalLayout(getContext());
            rootLayout.setOrientation(DirectionalLayout.HORIZONTAL);
            DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig(
                    ComponentContainer.LayoutConfig.MATCH_CONTENT,
                    ComponentContainer.LayoutConfig.MATCH_CONTENT);
            addComponent(rootLayout, config);
        } else if (clear) {
            rootLayout.removeAllComponents();
        }
        return rootLayout;
    }

    private void removeComponent() {
        for (int i = getChildCount() - 1; i > 0; i--) {
            removeComponentAt(i);
        }
        mBarSelectedListeners.removeIf(topBarInfoOnBarSelectedListener ->
                topBarInfoOnBarSelectedListener instanceof TopBar);
    }
}
